package oss

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
)

var (
	ErrInvalidPartNum = errors.New("invalid part number")
)

func (o OssAPI) InitiateMultipartUpload(objectName string, encodingType string, headers map[string]string) (*InitiateMultipartUploadResult, error) {
	supportHeaders := []string{
		"Cache-Control", "Content-Disposition", "Content-Encoding", "Expires",
		"x-oss-server-side-encryption",
	}

	for key, _ := range headers {
		if key != "" && !isValidName(key, supportHeaders) {
			return nil, ErrInvalidHeader
		}
	}

	path := "/" + objectName + "?uploads"
	v := url.Values{}
	if encodingType != "" {
		v.Set("encoding-type", encodingType)
	}

	h := http.Header{}
	for key, value := range headers {
		h.Set(key, value)
	}

	result := InitiateMultipartUploadResult{}
	err := o.httpPost(path, v, h, nil, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

func (o OssAPI) UploadPart(objectName string, partNum int, uploadId string) error {
	if partNum < 1 || partNum > 10000 {
		return ErrInvalidPartNum
	}

	v := url.Values{}
	v.Set("partNumber", fmt.Sprintf("%d", partNum))
	v.Set("uploadId", uploadId)

	path := "/" + objectName + "?" + v.Encode()

	return o.httpPut(path, nil, nil, nil, nil)
}

func (o OssAPI) UploadPartCopy(objectName string, partNum int, uploadId string, headers map[string]string) (*CopyPartResult, error) {
	if partNum < 1 || partNum > 10000 {
		return nil, ErrInvalidPartNum
	}

	supportHeaders := []string{
		"x-oss-copy-source", "x-oss-copy-source-range", "x-oss-copy-source-if-match", "x-oss-copy-source-if-none-match",
		"x-oss-copy-source-if-unmodified-since", "x-oss-copy-source-if-modified-since",
	}

	for key, _ := range headers {
		if isValidName(key, supportHeaders) {
			return nil, ErrInvalidHeader
		}
	}

	v := url.Values{}
	v.Set("partNumber", fmt.Sprintf("%d", partNum))
	v.Set("uploadId", uploadId)

	path := "/" + objectName + "?" + v.Encode()

	h := http.Header{}
	for key, value := range headers {
		h.Set(key, value)
	}

	result := CopyPartResult{}

	err := o.httpPut(path, nil, h, nil, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

func (o OssAPI) CompleteMultipartUpload(objectName string, uploadId string, encodingType string, parts []Part) (*CompleteMultipartUploadResult, error) {
	v := url.Values{}
	v.Set("uploadId", uploadId)
	path := "/" + objectName + "?" + v.Encode()

	v = url.Values{}
	if encodingType != "" {
		v.Set("encoding-type", encodingType)
	}

	upload := CompleteMultipartUpload{}
	for _, part := range parts {
		upload.Part = append(upload.Part, part)
	}

	result := CompleteMultipartUploadResult{}
	err := o.httpPost(path, v, nil, &upload, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

func (o OssAPI) AbortMultipartUpload(objectName string, uploadId string) error {
	v := url.Values{}
	v.Set("uploadId", uploadId)
	path := "/" + objectName + "?" + v.Encode()

	err := o.httpDelete(path, nil, nil, nil)
	return err
}

func (o OssAPI) ListMultipartUpload(delimiter string, maxUploads int, keyPrefix string,
	keyMarker string, uploadIdMarker string, encodingType string) (*ListMultipartUploadsResult, error) {

	v := url.Values{}

	if delimiter != "" {
		v.Set("delimiter", delimiter)
	}

	if maxUploads != 0 {
		v.Set("max-uploads", fmt.Sprintf("%d", maxUploads))
	}

	if keyPrefix != "" {
		v.Set("prefix", keyPrefix)
	}

	if keyMarker != "" {
		v.Set("key-marker", keyMarker)
	}

	if uploadIdMarker != "" {
		v.Set("upload-id-marker", uploadIdMarker)
	}

	if encodingType != "" {
		v.Set("encoding-type", encodingType)
	}

	path := "/?uploads"
	if len(v) > 0 {
		path = path + "&" + v.Encode()
	}

	result := ListMultipartUploadsResult{}
	err := o.httpGet(path, nil, nil, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

func (o OssAPI) ListParts(objectName string, uploadId string, maxParts int, partNumberMarker int, encodingType string) (*ListPartsResult, error) {
	v := url.Values{}
	v.Set("uploadId", uploadId)

	if maxParts != 0 {
		v.Set("max-parts", fmt.Sprintf("%d", maxParts))
	}

	if partNumberMarker != 0 {
		v.Set("part-number-marker", fmt.Sprintf("%d", partNumberMarker))
	}

	if encodingType != "" {
		v.Set("encoding-type", encodingType)
	}

	path := "/" + objectName + "?" + v.Encode()
	result := ListPartsResult{}
	err := o.httpGet(path, nil, nil, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}
